/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package luft27.comm;

import java.io.IOException;
import java.net.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author amalikov
 */
public class TCPStream implements SerialStream {

	public static final boolean SERVER = true;
	public static final boolean CLIENT = false;
	
	public TCPStream(String host, int port, String handshake, boolean server, int timeout) {
		super();
		this.host = host;
		this.port = port;
		this.handshake = handshake;
		this.server = server;
		this.timeout = timeout;
		sck = null;
		serverSck = null;
		opened = false;
	}	
	
	private void open() throws IOException {
		if (!opened) {
			if (server)
				createServerSocket();
			else {
				createClientSocket();
				sck.getOutputStream().write(handshake.getBytes());
			}

			opened = true;
		}

		checkClient();
	}

	@Override
	public void close() {
		opened = false;
		
		if (server && serverSck != null) {
			try {
				serverSck.close();
			} catch (IOException ex) {
				Logger.getLogger(TCPStream.class.getName()).log(Level.SEVERE, null, ex);
			}
			serverSck = null;
		}

		if (sck != null) {
			try {
				sck.close();
			} catch (IOException ex) {
				Logger.getLogger(TCPStream.class.getName()).log(Level.SEVERE, null, ex);
			}
			sck = null;
		}
	}

	@Override
	public int read(byte[] data, int ofs, int len) {
		try {
			open();
			return sck.getInputStream().read(data, ofs, len);
		} catch (SocketTimeoutException | ConnectException ex) {
		} catch (IOException ex) {
			Logger.getLogger(TCPStream.class.getName()).log(Level.SEVERE, null, ex);
		}
		
		return 0;
	}

	@Override
	public int read() {
		byte[] a = new byte[1];
		int res = read(a, 0, 1);
		return (res > 0) ? a[0] : -1;
	}

	@Override
	public void write(byte[] data, int ofs, int len) {
		try {
			open();
			sck.getOutputStream().write(data, ofs, len);
		} catch (SocketTimeoutException | ConnectException ex) {			
		} catch (SocketException ex) {
			close();
		} catch (IOException ex) {
			Logger.getLogger(TCPStream.class.getName()).log(Level.SEVERE, null, ex);
		}
	}

	@Override
	public void clean() {
		try {
			open();
			sck.getInputStream().skip(sck.getInputStream().available());
		} catch (SocketTimeoutException | ConnectException ex) {			
		} catch (IOException ex) {
			Logger.getLogger(TCPStream.class.getName()).log(Level.SEVERE, null, ex);
		}
	}
	
	private void createServerSocket() throws IOException {
		serverSck = new ServerSocket(port);
		serverSck.setReuseAddress(true);
		serverSck.setSoTimeout(timeout);
	}	
	
	private void createClientSocket() throws IOException {
		sck = new Socket(host, port);
		sck.setSoTimeout(timeout);
	}
	
	private void checkClient() throws IOException {
		if (!server || sck != null)
			return;
		sck = serverSck.accept();
		sck.setSoTimeout(timeout);
	}

	
	private final String host;
	private final int port;
	private final String handshake;
	private final boolean server;
	private final int timeout;
	private ServerSocket serverSck;
	private Socket sck;
	private boolean opened;
}
